home *** CD-ROM | disk | FTP | other *** search
- /*
- * CBLibrary - LoadSaveMT
- * Copyright (C) 2003 Chris Bazley
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
- /* Multi-tasking file loading/saving */
-
- /* ANSI library files */
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <stdbool.h>
- #include <limits.h>
-
- /* RISC OS library files */
- #include "kernel.h"
- #include "flex.h"
-
- /* Other headers */
- #include "msgtrans.h"
- #include "Macros.h"
- #include "NoBudge.h"
- #include "LoadSaveMT.h"
- #include "FopenCount.h"
- #include "hourglass.h"
-
- #define GRANULARITY 64
-
- /*
- This is a workaround for the fact that IO buffering sometimes runs out when accessing files with flex budge (and hence heap expansion) disabled:
- */
- #define STATIC_BUFFER
-
- /*
- The alternative is to pre-expand the heap by an unusually large amount before disabling flex budge:
- */
- #ifndef STATIC_BUFFER
- #define IO_HEAPSPACE 4096
- #else
- static char static_buffer[GRANULARITY];
- #endif
-
- typedef struct _fileop_state {
- FILE *f;
- long int read_pos;
- unsigned int mem_pos;
- unsigned int len;
- } fileop_state;
-
- extern _kernel_oserror shared_err_block;
-
- /* ----------------------------------------------------------------------- */
- /* Public functions */
-
- unsigned int get_loadsave_perc(FILE ***handle)
- {
- fileop_state *state = (fileop_state *)*handle;
- #ifndef NDEBUG
- {
- char string[255];
- sprintf(string, "report get_loadsave_perc mem_pos: %d len: %d", state->mem_pos, state->len);
- _kernel_oscli(string);
- }
- #endif
- if(state->len == 0)
- return 100; /* guard against divide-by-zero */
-
- if(state->mem_pos < (ULONG_MAX/100))
- return (unsigned int)(state->mem_pos*100) / state->len;
- else
- return (unsigned int)(((float)state->mem_pos*100) / state->len);
- }
-
- /* ----------------------------------------------------------------------- */
-
- _kernel_oserror *load_fileM(const char *filepath, flex_ptr buffer_anchor, const volatile bool *timeup, FILE ***handle, bool sprite)
- {
- fileop_state *state;
-
- if(*handle == NULL) {
- /* Starting afresh */
- #ifndef NDEBUG
- _kernel_oscli("report starting load");
- #endif
-
- state = (fileop_state *)malloc(sizeof(fileop_state));
- if(state == NULL) {
- WRITE_GERR(shared_err_block, "NoMem");
- return &shared_err_block; /* fail */
- }
-
- /* Get size of file */
- {
- _kernel_osfile_block inout;
- if(_kernel_osfile(17, filepath, &inout) == _kernel_ERROR) {
- free(state);
- return _kernel_last_oserror(); /* failure */
- }
- state->len = inout.start;
- }
- if(sprite)
- state->len += sizeof(unsigned int);
-
- /* Allocate buffer for data */
- if(!flex_alloc(buffer_anchor, state->len)) {
- free(state);
- WRITE_GERR(shared_err_block, "NoMem");
- return &shared_err_block; /* fail */
- }
- if(sprite) {
- /* write size of sprite area into the 1st word */
- *(unsigned int *)*buffer_anchor = state->len;
- /* and update write pointer to point past 1st word */
- state->mem_pos = sizeof(unsigned int);
- }
- else
- state->mem_pos = 0;
- state->f = NULL;
- state->read_pos = 0; /* start reading from beginning of file */
-
- } else {
- /* Continue from where we left off */
- state = (fileop_state *)*handle;
- #ifndef NDEBUG
- char string[255];
- sprintf(string, "report continuing load from mem_pos %d", state->mem_pos);
- _kernel_oscli(string);
- #endif
- }
-
- _kernel_last_oserror(); /* reset SCL's error recording */
-
- if(state->f == NULL) {
- /* (Re)open file */
- state->f = fopen_inc(filepath, "rb"); /* open for reading */
- if(state->f == NULL) {
- free(state);
- *handle = NULL; /* write back NULL pointer */
- THROW(_kernel_last_oserror()) /* any OS error? */
- WRITE_ERR_SUB1(shared_err_block, "OpenInFail", filepath);
- return &shared_err_block; /* fail */
- }
- fseek(state->f, state->read_pos, SEEK_SET); /* start reading data where we left off */
- }
-
- hourglass_on(); /* floppy discs can be real slow! */
- {
- void *ptr;
- unsigned int write_offset = state->mem_pos, len = state->len;
-
- #ifdef STATIC_BUFFER
- ptr = static_buffer;
- #else
- nobudge_register(IO_HEAPSPACE); /* protect pointer into flexblock */
- ptr = *buffer_anchor;
- #endif
-
- #ifndef NDEBUG
- if(*timeup)
- _kernel_oscli("report timeup before start");
- #endif
-
- /* Read chunks of data until time up */
- do {
- size_t chunk_size;
-
- if(write_offset > len)
- chunk_size = len - write_offset;
- else
- chunk_size = GRANULARITY;
-
- #ifdef STATIC_BUFFER
- /* Read chunk from file into static block */
- {
- size_t num_read = fread(ptr, sizeof(char), chunk_size, state->f);
- if(num_read > 0) {
- /* Copy chunk from static block to flex */
- nobudge_register(256);
- memcpy((void *)((unsigned int)*buffer_anchor + write_offset), ptr, num_read);
- nobudge_deregister();
- }
- if(num_read != chunk_size)
- break; /* EOF or read error */
- }
- #else
- /* Read chunk from file directly into flex block (budge is disabled) */
- if(fread((void *)((unsigned int)ptr + write_offset), sizeof(char), chunk_size, state->f) != chunk_size)
- break; /* EOF or read error */
- #endif
- write_offset += GRANULARITY;
- } while(*timeup == false);
-
- #ifndef STATIC_BUFFER
- nobudge_deregister();
- #endif
- state->mem_pos = write_offset;
- state->len = len;
- }
- hourglass_off();
-
- if(ferror(state->f)) {
- /* File error on fread() */
- #ifndef NDEBUG
- _kernel_oscli("report Aborting - file error on fread()");
- #endif
- fclose_dec(state->f);
- free(state);
- *handle = NULL; /* write back NULL pointer */
- THROW(_kernel_last_oserror()) /* any OS error? */
- WRITE_ERR_SUB1(shared_err_block, "ReadFail", filepath);
- return &shared_err_block; /* fail */
- }
-
- if(feof(state->f)) {
- /* Finished (got to end of input) */
- #ifndef NDEBUG
- _kernel_oscli("report Loading complete (EOF)");
- #endif
- fclose_dec(state->f);
- free(state);
- *handle = NULL; /* write back NULL pointer */
-
- } else {
- /* Stopped before EOF */
- #ifndef NDEBUG
- char string[255];
- sprintf(string, "report Pausing loading at mem_pos %d", state->mem_pos);
- _kernel_oscli(string);
- #endif
- if(fopen_num() >= FOPEN_MAX) {
- /* if we have no spare file handles then close file */
- state->read_pos = ftell(state->f);
- fclose_dec(state->f);
- state->f = NULL;
- }
- *handle = (FILE **)state; /* write back pointer to state */
- }
- return NULL; /* no error */
- }
-
- /* ----------------------------------------------------------------------- */
-
- _kernel_oserror *save_fileM(const char *filepath, int filetype, flex_ptr buffer_anchor, const volatile bool *timeup, FILE ***handle, bool sprite)
- {
- fileop_state *state;
-
- {
- char *open_mode;
- if(*handle == NULL) {
- /* Starting afresh */
- #ifndef NDEBUG
- _kernel_oscli("report starting save");
- #endif
- state = (fileop_state *)malloc(sizeof(fileop_state));
- if(state == NULL) {
- WRITE_GERR(shared_err_block, "NoMem");
- return &shared_err_block; /* fail */
- }
- state->len = flex_size(buffer_anchor);
- if(sprite)
- /* skip the 1st word (size of sprite area) */
- state->mem_pos = sizeof(unsigned int);
- else
- state->mem_pos = 0;
-
- state->f = NULL;
- open_mode = "wb"; /* open for writing */
-
- } else {
- /* Continue from where we left off */
- state = (fileop_state *)*handle;
- #ifndef NDEBUG
- char string[255];
- sprintf(string, "report continuing saving from mem_pos %d", state->mem_pos);
- _kernel_oscli(string);
- #endif
- open_mode = "ab"; /* open for appending */
- }
-
- _kernel_last_oserror(); /* reset SCL's error recording */
-
- if(state->f == NULL) {
- /* (Re)open file */
- state->f = fopen_inc(filepath, open_mode);
- if(state->f == NULL) {
- free(state);
- *handle = NULL; /* write back NULL pointer */
- THROW(_kernel_last_oserror()) /* any OS error? */
- WRITE_ERR_SUB1(shared_err_block, "OpenOutFail", filepath);
- return &shared_err_block; /* fail */
- }
- }
- }
-
- hourglass_on(); /* floppy discs can be real slow! */
- {
- void *ptr;
- unsigned int read_offset = state->mem_pos, len = state->len;
-
- #ifdef STATIC_BUFFER
- ptr = static_buffer;
- #else
- nobudge_register(IO_HEAPSPACE); /* protect pointer into flexblock */
- ptr = *buffer_anchor;
- #endif
-
- #ifndef NDEBUG
- if(*timeup)
- _kernel_oscli("report timeup before start");
- #endif
-
- /* Write chunks of data until we run out or time up */
- while(read_offset < len) {
- unsigned int chunk_size;
-
- if(read_offset + GRANULARITY > len)
- chunk_size = len - read_offset;
- else
- chunk_size = GRANULARITY;
-
- #ifdef STATIC_BUFFER
- /* Copy chunk from flex to static block */
- nobudge_register(256);
- memcpy(ptr, (void *)((unsigned int)*buffer_anchor + read_offset), chunk_size);
- nobudge_deregister();
-
- /* Write contents of static block out to file */
- if(fwrite(ptr, sizeof(char), chunk_size, state->f) != chunk_size)
- #else
- /* Write chunk of flex block out to file (budge is disabled) */
- if(fwrite((void *)((unsigned int)ptr + read_offset), sizeof(char), chunk_size, state->f) != chunk_size)
- #endif
- break; /* write error */
-
- read_offset += GRANULARITY;
- if(*timeup == true)
- break;
- } /* endwhile */
-
- state->mem_pos = read_offset;
- state->len = len;
-
- #ifndef STATIC_BUFFER
- nobudge_deregister();
- #endif
- }
- hourglass_off();
-
- if(ferror(state->f)) {
- #ifndef NDEBUG
- _kernel_oscli("report Aborting - file error on fwrite()");
- #endif
- fclose_dec(state->f);
- free(state);
- *handle = NULL; /* write back NULL pointer */
- THROW(_kernel_last_oserror()) /* any OS error? */
- WRITE_ERR_SUB1(shared_err_block, "WriteFail", filepath);
- return &shared_err_block; /* fail */
- }
-
- if(state->mem_pos >= state->len) {
- /* Finished (got to end of input) */
- #ifndef NDEBUG
- _kernel_oscli("report Saving complete");
- #endif
- fclose_dec(state->f);
- free(state);
- *handle = NULL; /* write back NULL pointer */
-
- /* Set file type */
- {
- _kernel_osfile_block inout;
- inout.load = filetype;
- if(_kernel_osfile(18, filepath, &inout) == _kernel_ERROR)
- return _kernel_last_oserror(); /* failure */
- }
- } else {
- /* Assume stopped cos of time out */
- #ifndef NDEBUG
- char string[255];
- sprintf(string, "report Pausing save at mem_pos %d", state->mem_pos);
- _kernel_oscli(string);
- #endif
- if(fopen_num() >= FOPEN_MAX) {
- /* if we have no spare file handles then close file */
- fclose_dec(state->f);
- state->f = NULL;
- }
- *handle = (FILE **)state; /* write back pointer to state */
- }
-
- return NULL; /* success */
- }
-